package ubadbtools.deadlockAnalyzer.grafo;

import java.util.*;

// 1994 - Mautas

public class Grafo {
	static int granularity = 10;
	boolean esdigrafo;
	NodoArray[] nodosarray;
	int maxnodos;

	public Grafo(boolean tipo) {
		esdigrafo = tipo;
		nodosarray = new NodoArray[granularity + 1];
		maxnodos = granularity;
	}

	private NodoArray[] nodoArrayGrow(NodoArray[] a, int s) {
		int newLength = s + ((s - maxnodos) + 1) % granularity;
		NodoArray[] newArray = new NodoArray[newLength];
		System.arraycopy(a, 0, newArray, 0, maxnodos);
		maxnodos = newLength;
		return newArray;
	}

	public boolean existeNodo(int v) {
		if (v <= maxnodos)
			return (!(nodosarray[v] == null));
		else
			return false;
	}

	public void agregarNodo(int v) {

		if (v > maxnodos)
			nodosarray = nodoArrayGrow(nodosarray, v);

		if (!this.existeNodo(v))
			nodosarray[v] = new NodoArray();

	}

	public boolean existeEje(int v, int w) {

		if (existeNodo(v) && existeNodo(w))
			if (esdigrafo)
				return (nodosarray[v].existe(w) && nodosarray[v].seDirigeA(w));
			else
				return (nodosarray[v].existe(w));
		else
			return false;
	}

	public void agregarEje(int v, int w) {

		if (existeNodo(v) && existeNodo(w)) {
			nodosarray[v].agregar(w);
			nodosarray[w].agregar(v);
			if (esdigrafo) {
				nodosarray[v].hacia(w, true);
				nodosarray[w].desde(v, true);
			}
		} else {
			System.out
					.print("Error (99) Agregar eje con un nodo no existente.");
			// RunError(99);
		}
	}

	public void eliminarNodo(int v) {
		int i;
		ListaAdy p;

		if (existeNodo(v)) {

			p = nodosarray[v].adys;
			while (!p.vacia()) {
				nodosarray[p.primero().nnodo].eliminar(v);
				p = p.proximo;
			}
			nodosarray[v] = null;
			i = maxnodos;
			while ((nodosarray[i] == null) && (i > 1)) {
				i = i + 1;
			}
			if (i < maxnodos - granularity) {
				if (i <= granularity) {
					maxnodos = granularity;
				} else {

					if ((i % granularity) != 0)
						maxnodos = i + granularity - (i % granularity);
					else
						maxnodos = i;
				}
			}
		}
	}

	public void eliminarEje(int v, int w) {
		if (this.existeNodo(v) && this.existeNodo(w)) {
			if (!esdigrafo || (esdigrafo && (!(nodosarray[w].seDirigeA(v))))) {
				nodosarray[v].eliminar(w);
				nodosarray[w].eliminar(v);
			} else {
				if (esdigrafo && (nodosarray[w].seDirigeA(v))) {
					nodosarray[v].hacia(w, false);
					nodosarray[w].desde(v, false);
				}
			}
		} else {
			System.out
					.print("Error (98) Eliminar eje con un nodo no existente.");
			// RunError(98);
		}
	}

	public int cantidadNodos() {
		int i, aux;
		aux = 0;
		for (i = 1; i <= maxnodos; i++)
			if (existeNodo(i))
				aux++;
		return aux;
	}

	public int cantidadEjes() {
		int i, aux;
		aux = 0;
		for (i = 1; i <= maxnodos; i++) {
			if (existeNodo(i))
				if (esdigrafo)
					aux = aux + nodosarray[i].gradoIn()
							+ nodosarray[i].gradoOut();
				else
					aux = aux + nodosarray[i].grado();
		}
		aux = aux / 2;
		return aux;
	}

	public int grado(int v) {
		return nodosarray[v].grado();
	}

	public int gradoIn(int v) {

		if (esdigrafo)
			return nodosarray[v].gradoIn();
		else {
			System.out
					.print("Error (96) Los grafos simples no tienen ni GradoIn ni GradoOut.");
			// RunError(96);
		}
		return -1;
	}

	public int gradoOut(int v) {
		if (esdigrafo)
			return nodosarray[v].gradoOut();
		else {
			System.out
					.print("Error (96) Los grafos simples no tienen ni GradoIn ni GradoOut.");
			// RunError(96);
		}
		return -1;
	}

	public void asociarDatosNodo(int v, Object p) {

		if (existeNodo(v))
			nodosarray[v].asociarDato(p);
		else {
			System.out
					.print("Error (95) Obtener o Asociar Datos de un nodo no existente.");
			// RunError(95);
		}
	}

	public Object obtenerDatosAsociadosNodo(int v) {

		if (existeNodo(v))
			return nodosarray[v].obtenerDato();
		else {
			System.out
					.print("Error (95) Obtener o Asociar Datos de un nodo no existente.");
			// RunError(95);
		}
		return null;
	}

	public void asociarDatosEje(int v, int w, Object p) {

		if (existeEje(v, w))
			nodosarray[v].asociarDatoEje(w, p);
		if (!esdigrafo)
			nodosarray[w].asociarDatoEje(v, p);
		else {
			System.out
					.print("Error (94) Obtener o Asociar Datos de un eje no existente.");
			// RunError(94);
		}
	}

	public Object obtenerDatosAsociadosEje(int v, int w) {

		if (existeEje(v, w))
			return nodosarray[v].obtenerDatoEje(w);
		else {
			System.out
					.println("Error (94) Obtener o Asociar Datos de un eje no existente.");
			// RunError(94);
		}
		return null;
	}

	public boolean tieneCicloEmpezandoEn(int v) {

		int proximo;
		ArrayList<Integer> visitados = new ArrayList<Integer>();
		visitados.add(v);
		IteradorSucesores proximos = new IteradorSucesores(this, v);
		while (proximos.hayMas()) {
			proximo = proximos.obtenerNodo();
			if (visitados.contains(proximo))
				return true;
			else
				visitados.add(proximo);
		}

		return false;

	}

}